﻿using System;

namespace OpenCube.Model.Primitives
{
	/// <summary>
	/// Represents a time duration in the game.
	/// </summary>
	public struct GameTimeSpan
	{
		public GameTimeSpan(long gameTicks)
		{
			_ticks = gameTicks;
		}

		/// <summary>
		/// The number of game ticks per second.
		/// </summary>
		public const long TicksPerSecond = 20;

		public const long TicksPerGameDay = 24000;

		/// <summary>
		/// The minimal game timespan possible
		/// </summary>
		public static readonly GameTimeSpan MinValue = new GameTimeSpan(TimeSpan.MinValue.Ticks / GameTimeTicksOffset);

		/// <summary>
		/// The maximal game timespan possible
		/// </summary>
		public static readonly GameTimeSpan MaxValue = new GameTimeSpan(TimeSpan.MaxValue.Ticks / GameTimeTicksOffset);

		// system ticks sec (10,000,000) / game ticks sec (20) = 500,000
		private const long RealTimeTicksOffset = TimeSpan.TicksPerSecond / TicksPerSecond;

		// system ticks day (864,000,000,000) / game ticks day (24,000) = 36,000,000
		private const long GameTimeTicksOffset = TimeSpan.TicksPerDay / TicksPerGameDay;

		private readonly long _ticks;

		/// <summary>
		/// Gets the number of game ticks (20 per second)
		/// </summary>
		public long Ticks
		{
			get { return _ticks; }
		}

		#region Component Values

		public int Seconds
		{
			get { return ToGameTimeSpan().Seconds; }
		}

		public int Minutes
		{
			get { return ToGameTimeSpan().Minutes; }
		}

		public int Hours
		{
			get { return ToGameTimeSpan().Hours; }
		}

		public int Days
		{
			get { return ToGameTimeSpan().Days; }
		}

		#endregion
		#region Total Values

		public double TotalSeconds
		{
			get { return ToGameTimeSpanForTotal().TotalSeconds; }
		}

		public double TotalMinutes
		{
			get { return ToGameTimeSpanForTotal().TotalMinutes; }
		}

		public double TotalHours
		{
			get { return ToGameTimeSpanForTotal().TotalHours; }
		}

		public double TotalDays
		{
			get { return ToGameTimeSpanForTotal().TotalDays; }
		}

		#endregion
		#region Conversion

		public TimeSpan ToRealTimeSpan()
		{
			// create a system timespan by multiplying the ticks a preconfigured number 
			return new TimeSpan(_ticks * RealTimeTicksOffset);
		}

		private TimeSpan ToGameTimeSpan()
		{
			// create in game timespan by multiplying the ticks a preconfigured number
			// also add 6000 (6 hours) so 6000 maps to Noon (12000) and 18000 maps to night (24000).
			return new TimeSpan((_ticks + 6000) * GameTimeTicksOffset);
		}

		private TimeSpan ToGameTimeSpanForTotal()
		{
			// create in game timespan by multiplying the ticks a preconfigured number
			return new TimeSpan(_ticks* GameTimeTicksOffset);
		}

		public static GameTimeSpan FromRealTimeSpan(TimeSpan timeSpan)
		{
			// create a game timespan by dividing the ticks by a preconfigured number
			return new GameTimeSpan((timeSpan.Ticks / RealTimeTicksOffset) - 6000);
		}

		private static GameTimeSpan FromInGameTimeSpan(TimeSpan timeSpan)
		{
			return new GameTimeSpan(timeSpan.Ticks / GameTimeTicksOffset);
		}

		public override string ToString()
		{
			// lets use the system timespan to do all the hard work of formatting.
			return ToGameTimeSpan().ToString();
		}

		#endregion
	}
}